package app;

/**
 * Created with IntelliJ IDEA.
 * User: Administrator
 * Date: 2018/7/21 0021
 * Time: 20:13
 * To change this template use File | Settings | File Templates.
 * Description:
 */
public class SolverService {
    //题目
    private int[][] question = new int[9][9];
    //解题矩阵
    private final int[][] solution = new int[9][9];

    public int[][] getQuestion() {
        return question;
    }

    public void setQuestion(int[][] question) {
        this.question = question;

    }

    public int[][] getSolution() {
        return solution;
    }

    /**
     * 输出答案
     */
    private void populate() {
        for (int i = 0; i < 9; i++) {
            System.arraycopy(this.question[i], 0, this.solution[i], 0, 9);
        }
    }

    /**
     * 找出位置所在方形的中心点
     *
     * @param n 行列号
     * @return 中心的行列号
     */
    private int getCenter(int n) {
        return switch (n) {
            case 0, 1, 2 -> 1;
            case 3, 4, 5 -> 4;
            case 6, 7, 8 -> 7;
            default -> -1;
        };
    }

    /**
     * 检查矩阵的完整性
     *
     * @return 填入的数字是否合法
     */
    private boolean checkIntegrity(int current_i, int current_j, int num) {
        // 第一步检查所在行
        for (int i = 0; i < 9; i++) {
            if (this.question[i][current_j] == num) {
                return false;
            }
        }
        // 第二步检查所在列
        for (int j = 0; j < 9; j++) {
            if (this.question[current_i][j] == num) {
                return false;
            }
        }
        // 第三步是检查所在的3*3正方形区块
        // 思路是先找出位置所在区块的中心点，然后获取包括中心的9个元素，再查重
        // 所以要建立一个012->1，345->4，678->7的映射，这个可以单独写一个getCenter来完成
        int center_i = getCenter(current_i), center_j = getCenter(current_j);
        int[] sub_array = new int[9];// subarray是存放小方形数字的数组;
        sub_array[0] = this.question[center_i - 1][center_j - 1];
        sub_array[1] = this.question[center_i][center_j - 1];
        sub_array[2] = this.question[center_i + 1][center_j - 1];
        sub_array[3] = this.question[center_i - 1][center_j];
        sub_array[4] = this.question[center_i][center_j];
        sub_array[5] = this.question[center_i + 1][center_j];
        sub_array[6] = this.question[center_i - 1][center_j + 1];
        sub_array[7] = this.question[center_i][center_j + 1];
        sub_array[8] = this.question[center_i + 1][center_j + 1];
        for (int i = 0; i < 9; i++) {// 搜索所在小正方形有无重复数字
            if (sub_array[i] == num) {
                return false;
            }
        }
        return true;
    }

    /**
     * 尝试填入矩阵
     */
    public void fill(int startIndex) {
        int next_Index;// 待插入数字的索引
        int next_i = 9, next_j = 9;// 待插入的位置
        //从当前位置开始逐个向前尝试
        for (next_Index = startIndex; next_Index < 81; next_Index++) {
            //尝试插入位置的行与列坐标
            int pos_x = next_Index / 9, pos_y = next_Index % 9;
            if (this.question[pos_x][pos_y] == 0) {// 0表示这个位置还没有填写过
                next_i = pos_x;
                next_j = pos_y;
                break;
            }
        }
        if (next_Index > 80) {// 已经尝试过所有位置，不再继续
            populate();
            return;
        }
        for (int num = 1; num <= 9; num++) {
            if (this.checkIntegrity(next_i, next_j, num)) {// 如果此位置尝试插入的数字合法
                this.question[next_i][next_j] = num;//填入这个数字
                fill(next_Index + 1);//递归尝试下一位置
                this.question[next_i][next_j] = 0;//恢复为未填状态，以便检验下一个尝试的数字
            }
        }
    }
}
